package com.heima.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.common.constants.search.SearchConstants;
import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.model.search.vos.SearchArticleVo;
import com.heima.model.threadlocal.AppThreadLocalUtils;
import com.heima.model.user.pojos.ApUser;
import com.heima.search.service.ApUserSearchService;
import com.heima.search.service.ArticleSearchService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class ArticleSearchServiceImpl implements ArticleSearchService {
    @Autowired
    private RestHighLevelClient client;
    @Value("${file.minio.readPath}")
    private String readPath;
    @Autowired
    private ApUserSearchService apUserSearchService;

    @Override
    public ResponseResult search(UserSearchDto dto) {
        //判断数据是否合法
        if (StringUtils.isBlank(dto.getSearchWords())) {
            CustomException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        //初始化分页查询条件
        if (dto.getPageNum() == 0) {
            dto.setPageNum(1);
        }
        if (dto.getPageSize() < 1) {
            dto.setPageSize(10);
        }
        if (dto.getMinBehotTime() == null) {
            dto.setMinBehotTime(new Date());
        }
        //获取当前登录的用户
        ApUser user = AppThreadLocalUtils.getUser();
        if (user == null || user.getId() == null) {
            CustomException.cust(AppHttpCodeEnum.NEED_LOGIN);
        }
        //记录当前搜索历史记录
        dto.setEntryId(user.getId());
        //判断是不是首页查询
        if (dto.getFromIndex() == 0) {
            //异步调用保存用户输入关键词记录
            apUserSearchService.insert(dto);
        }
        SearchRequest request = getSearchRequest(dto);
        try {
            //调用客户端执行查询
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            List<SearchArticleVo> searchArticleVos = getSearchArticleVos(response);
            return ResponseResult.okResult(searchArticleVos);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("es查询出现错误，错误原因：{}", e.getMessage());
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "es查询出错！！！");
        }
    }

    /**
     * 获取结果集
     */
    private List<SearchArticleVo> getSearchArticleVos(SearchResponse response) {
        List<SearchArticleVo> searchArticleVos = new ArrayList<>();//查询结果集
        //处理查询结果
        SearchHits hits = response.getHits();
        //获取查询文档
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit searchHit : searchHits) {
            //将查询的文档转换成json字符串
            String sourceAsString = searchHit.getSourceAsString();
            //将json格式转化成SearchArticleVo对象
            SearchArticleVo searchArticleVo = JSON.parseObject(sourceAsString, SearchArticleVo.class);
            //处理静态url
            if (StringUtils.isNotBlank(searchArticleVo.getStaticUrl())) {
                searchArticleVo.setStaticUrl(readPath + searchArticleVo.getStaticUrl());
            }
            //高亮处理
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            HighlightField title = highlightFields.get("title");
            if (title != null) {
                //高亮的结果是一个片段数据
                Text[] fragments = title.getFragments();
                String join = StringUtils.join(fragments);
                searchArticleVo.setTitle(join);
            }
            searchArticleVos.add(searchArticleVo);
        }
        return searchArticleVos;
    }

    /**
     * 获取请求对象
     */
    private SearchRequest getSearchRequest(UserSearchDto dto) {
        //获取查询请求对象
        SearchRequest request = new SearchRequest(SearchConstants.ARTICLE_INDEX_NAME);
        //获取条件查询对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //构建查询条件
        // boolean 查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 分词查询
        boolQueryBuilder.must(QueryBuilders.matchQuery("title", dto.getSearchWords()));
        // 范围查询
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("publishTime").lt(dto.getMinBehotTime()));
        sourceBuilder.query(boolQueryBuilder);
        // 高亮查询
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<span style='color:pink;font-size: inherit'>");
        highlightBuilder.postTags("</span>");
        sourceBuilder.highlighter(highlightBuilder);
        // 分页查询
        sourceBuilder.from(0);
        sourceBuilder.size(dto.getPageSize());
        // 排序
        sourceBuilder.sort("publishTime", SortOrder.DESC);
        //向请求中注入查询条件
        request.source(sourceBuilder);
        return request;
    }

    /**
     * 添加索引文章
     */
    @Override
    public void saveArticle(SearchArticleVo article) {
        //获取请求
        IndexRequest request = new IndexRequest(SearchConstants.ARTICLE_INDEX_NAME);
        request.id(String.valueOf(article.getId()));
        String s = JSON.toJSONString(article);
        request.source(s, XContentType.JSON);
        //发送请求
        try {
            IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("添加索引库文章数据失败：{}", article);
        }
    }

    /**
     * 删除索引文章
     */
    @Override
    public void deleteArticle(Long articleId) {
        //获取请求
        DeleteRequest request = new DeleteRequest(SearchConstants.ARTICLE_INDEX_NAME);
        try {
            DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("删除索引库文章数据失败：{}", articleId);
        }
    }
}
